package com.water.skin


import android.app.Activity
import android.content.Context
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.content.res.AssetManager
import android.content.res.Resources
import android.os.AsyncTask
import android.text.TextUtils
import android.util.Log
import android.view.View
import com.water.skin.attr.SkinAttrSupport
import com.water.skin.attr.SkinAttrSupport.addSkinViews
import com.water.skin.attr.SkinAttrSupport.getSkinViews
import com.water.skin.attr.SkinView
import com.water.skin.callback.ISkinChangingCallback
import com.water.skin.utils.L.e
import com.water.skin.utils.PrefUtils
import java.io.File
import androidx.fragment.app.Fragment

class SkinManager private constructor() {

    private object SingletonHolder {
        val instance by lazy { SkinManager() }
    }

    private lateinit var mContext: Context

    private var mResources: Resources? = null

    private lateinit var mResourceManager: ResourceManager

    private val mPrefUtils: PrefUtils by lazy {
        PrefUtils(mContext)
    }
    private var usePlugin = false
    private var mSuffix: String? = ""
    private var mCurPluginPath: String? = null
    private var mCurPluginPkg: String? = null

    private val mActivities = mutableListOf<Activity>()

    private val mFragments = mutableListOf<Fragment>()

    private val mViews = mutableListOf<View>()

    fun init(context: Context) {
        mContext = context.applicationContext

        val skinPluginPath = mPrefUtils.pluginPath
        val skinPluginPkg = mPrefUtils.pluginPkgName
        mSuffix = mPrefUtils.suffix

        if (!validPluginParams(skinPluginPath, skinPluginPkg)) return

        try {
            loadPlugin(skinPluginPath, skinPluginPkg!!, mSuffix)
            mCurPluginPath = skinPluginPath
            mCurPluginPkg = skinPluginPkg
        } catch (e: Exception) {
            mPrefUtils.clear()
            e.printStackTrace()
        }
    }

    private fun getPackageInfo(skinPluginPath: String): PackageInfo? {
        val pm = mContext.packageManager
        return pm.getPackageArchiveInfo(skinPluginPath, PackageManager.GET_ACTIVITIES)
    }

    @Throws(Exception::class)
    private fun loadPlugin(skinPath: String?, skinPkgName: String, suffix: String?) {
        val assetManager = AssetManager::class.java.newInstance()
        val addAssetPath = assetManager.javaClass.getMethod("addAssetPath", String::class.java)
        addAssetPath.invoke(assetManager, skinPath)
        val superRes = mContext.resources
        mResources = Resources(assetManager, superRes.displayMetrics, superRes.configuration)
        mResourceManager = ResourceManager(mResources!!, skinPkgName, suffix)
        usePlugin = true
    }

    private fun validPluginParams(skinPath: String?, skinPkgName: String?): Boolean {
        if (skinPath.isNullOrEmpty()) {
            return false
        }
        val file = File(skinPath)
        if (!file.exists()) return false

        skinPkgName?.let { pkgName ->
            val info = getPackageInfo(skinPath)
            info?.let {
                return it.packageName == pkgName
            }
        }
        return true
    }


    private fun checkPluginParamsThrow(skinPath: String, skinPkgName: String?) {
        require(
            validPluginParams(
                skinPath,
                skinPkgName
            )
        ) { "skinPluginPath or skinPkgName not valid ! " }
    }

    fun removeAnySkin() {
        e("removeAnySkin")
        clearPluginInfo()
        notifyChangedListeners()
    }

    fun needChangeSkin(): Boolean {
        return usePlugin || !TextUtils.isEmpty(mSuffix)
    }

    fun resourceManager(): ResourceManager {
        if (!usePlugin) {
            mResourceManager =
                ResourceManager(mContext.resources, mContext.packageName, mSuffix)
        }
        return mResourceManager
    }

    /**
     * 应用内换肤，传入资源区别的后缀
     *
     * @param suffix
     */
    fun changeSkin(suffix: String?) {
        clearPluginInfo() //clear before
        mSuffix = suffix
        mPrefUtils.putPluginSuffix(suffix)
        notifyChangedListeners()
    }

    private fun clearPluginInfo() {
        mCurPluginPath = null
        mCurPluginPkg = null
        usePlugin = false
        mSuffix = null
        mPrefUtils.clear()
    }

    private fun updatePluginInfo(skinPluginPath: String, pkgName: String, suffix: String?) {
        mPrefUtils.putPluginPath(skinPluginPath)
        mPrefUtils.putPluginPkg(pkgName)
        mPrefUtils.putPluginSuffix(suffix)
        mCurPluginPkg = pkgName
        mCurPluginPath = skinPluginPath
        mSuffix = suffix
    }

    fun changeSkin(
        skinPluginPath: String,
        skinPluginPkg: String? = null,
        callback: ISkinChangingCallback? = null
    ) {
        changeSkin(skinPluginPath, skinPluginPkg, null, callback)
    }

    /**
     * 根据suffix选择插件内某套皮肤，默认为""
     *
     * @param skinPluginPath
     * @param skinPluginPkg
     * @param suffix
     * @param callback
     */
    fun changeSkin(
        skinPluginPath: String,
        skinPluginPkg: String?,
        suffix: String?,
        callback: ISkinChangingCallback?
    ) {

        e("changeSkin = $skinPluginPath , $skinPluginPkg")

        val skinChangingCallback: ISkinChangingCallback =
            callback ?: ISkinChangingCallback.DEFAULT_SKIN_CHANGING_CALLBACK

        skinChangingCallback.onStart()
        try {
            checkPluginParamsThrow(skinPluginPath, skinPluginPkg)
        } catch (e: IllegalArgumentException) {
            skinChangingCallback.onError(RuntimeException("checkPlugin occur error"))
            return
        }

        /*object : AsyncTask<Void, Void, Int>() {
            override fun doInBackground(vararg params: Void): Int {
                e("doInBackground")
                return try {
                    loadPlugin(skinPluginPath, skinPluginPkg, suffix)
                    1
                } catch (e: Exception) {
                    e.printStackTrace()
                    0
                }
            }

            override fun onPostExecute(res: Int) {
                if (res == 0) {
                    skinChangingCallback.onError(RuntimeException("loadPlugin occur error"))
                    return
                }
                try {
                    updatePluginInfo(skinPluginPath, skinPluginPkg, suffix)
                    notifyChangedListeners()
                    skinChangingCallback.onComplete()
                } catch (e: Exception) {
                    e.printStackTrace()
                    skinChangingCallback.onError(e)
                }
            }
        }.execute()*/

        try {
            getPackageInfo(skinPluginPath)?.let {
                val packageName = it.packageName
                loadPlugin(skinPluginPath, packageName, suffix)
                updatePluginInfo(skinPluginPath, packageName, suffix)
                notifyChangedListeners()
                skinChangingCallback.onComplete()
            }
        } catch (e: Exception) {
            e.printStackTrace()
            skinChangingCallback.onError(e)
        }
    }

    private fun apply(activity: Activity) {
        val skinViews = getSkinViews(activity)
        for (skinView in skinViews) {
            skinView.apply()
        }
    }

    private fun apply(fragment: Fragment) {
        val skinViews = getSkinViews(fragment)
        for (skinView in skinViews) {
            skinView.apply()
        }
    }

    private fun apply(view: View) {
        val skinViews = getSkinViews(view)
        for (skinView in skinViews) {
            skinView.apply()
        }
    }

    fun register(activity: Activity) {
        mActivities.add(activity)
        activity.findViewById<View>(android.R.id.content).post { apply(activity) }
    }

    fun register(fragment: Fragment) {
        mFragments.add(fragment)
        fragment.view?.let {
            it.post { apply(fragment) }
        }
    }

    fun register(view: View) {
        mViews.add(view)
        view.post { apply(view) }
    }

    fun unregister(activity: Activity) {
        mActivities.remove(activity)
    }

    fun unregister(fragment: Fragment) {
        mFragments.remove(fragment)
    }

    fun unregister(view: View) {
        mViews.remove(view)
    }

    fun notifyChangedListeners() {
        for (activity in mActivities) {
            apply(activity)
        }

        for (fragment in mFragments) {
            apply(fragment)
        }

        for (view in mViews) {
            apply(view)
        }
    }

    /**
     * apply for dynamic construct view
     *
     * @param view
     */
    fun injectSkin(view: View?) {
        val skinViews = mutableListOf<SkinView>()
        addSkinViews(view!!, skinViews)
        for (skinView in skinViews) {
            skinView.apply()
        }
    }

    /**
     * 指定需要换插件皮肤的view.
     */
    fun skinTag(view: View, viewTag: Any): SkinManager {
        SkinAttrSupport.changeViewSkinTag(view, viewTag)
        return getInstance()
    }

    companion object {
        fun getInstance() = SingletonHolder.instance
    }
}